<?php
require_once 'TaskError.php';

/**异步任务
 * Class TaskProgress
 */
class TaskProgress
{
    /**
     * @var int 进程数
     */
    private $worker_num = 1;

    /** 任务回调
     * @var callable
     */
    private $taskCallable;

    /**
     * @var int 当前工作的子进程数
     */
    private $working_worker_num;

    /**任务错误类
     * @var TaskError
     */
    protected $taskError;

    /** 设置子进程数
     * @param int $worker_num
     * @return $this
     */
    public function setWorkerNum(int $worker_num)
    {
        if ($worker_num <= 0 ) $worker_num = 1;
        $this->worker_num = $worker_num;
        return $this;
    }

    /** 设置任务处理的闭包函数
     * @param callable $callable
     * @return $this
     */
    public function setTaskCallAble(callable $callable)
    {
        $this->taskCallable = $callable;
        return $this;
    }

    /**自定义任务错误日志处理类
     * @param TaskError $taskError
     * @return $this
     */
    public function setTaskError(TaskError $taskError)
    {
        $this->taskError = $taskError;
        return $this;
    }

    /**
     * 任务执行入口
     */
    public function run(callable $callable = null)
    {
        if (is_callable($callable)) {
            $this->setTaskCallAble($callable);
        }
        //监听信号
        $this->listenSignal();
        //做任务
        $this->doTask();
    }


    /**创建子进程执行任务
     * @throws Exception
     */
    public function doTask()
    {
        for ($i= 1; $i<= $this->worker_num; $i++) {
            //创建子进程
            $this->createChildProgress();
            //记录子进程数
            ++$this->working_worker_num;
        }
    }

    /**定义任务的子进程
     * @throws Exception
     */
    protected function createChildProgress()
    {
        if (!$this->taskCallable) {
            $this->error('未找到即将处理的闭包函数，请调用TaskProgress::setTaskCallAble声明');
            return false;
        }
        $progress = new \Swoole\Process(function(\Swoole\Process $process) {
            try {
                $this->doTaskCallable($process);
            } catch (\Exception $e) {
                $this->error($e);;
                $process->exit();
            }
        });
        //子进程开始执行
        $progress->start();
    }

    /**异步执行 用户指定的任务
     * @param \Swoole\Process $process
     */
    protected function doTaskCallable(\Swoole\Process &$process)
    {
        call_user_func($this->taskCallable);
        $process->exit();
    }

    /**输出一个错误日志
     * @param mixed $e
     */
    public function error($e) {
        $this->getTaskError()->log($e);
    }

    /**获取错误日志处理类
     * @return TaskError
     */
    public function getTaskError()
    {
        if (!$this->taskError) {
            //自创一个错误日志处理类
            $this->setTaskError(new TaskError('../log/task_error.txt'));
        }
        return $this->taskError;
    }

    /**
     * 监听信号
     */
    protected function listenSignal()
    {
        //异步信号监听时，才生效
        \Swoole\Process::signal(SIGCHLD, function() {
            //监听子进程退出
            $this->watchChildExit();
        });
        \Swoole\Process::signal(SIGTERM, function() {
            //监听子进程退出
            $this->watchChildExit();
        });
    }

    /**
     * 监听子进程退出
     */
    protected function watchChildExit()
    {
        //异步 回收子进程时
        while(\Swoole\Process::wait(false)) {
            //当前子进程数自减
            $this->working_worker_num -- ;
        }
        exit();
    }
}
